package agent.ccs
import scala.collection.mutable.HashMap
import cwb.Value
import cwb.Error
import cwb.Command


sealed trait Relabeling {
  def relabellingString: String
  def getLabel(s: Symbol): Option[Act]
  def getMap: HashMap[Symbol, Act]
  def getLitRelabel: LitRelabel
}

case class LitRelabel(labelMap: HashMap[Symbol, Act]) extends Relabeling {
  
  def getLabel(s: Symbol): Option[Act] = {
    labelMap.get(s)
  }
  
  override
  def equals(rhs: Any) = {
    rhs match {
      case rel: LitRelabel => rel.labelMap == labelMap
      case _ => false
    }
  }

  def getMap() = labelMap
  
  def getLitRelabel() = this
  
  override
  def hashCode() = 29

  
  def relabellingString: String = {
    // List of (Symbol, Act) pairs sorted by string representation of symbol.
    val rels = labelMap.toSeq.sortBy(_._1.name)
    var string = "["
    val relabels = for ((s, a) <- rels) yield a.mkstr() + "/" + s.name
    string += relabels.mkString(",")
    string += "]"
    string
  }
}

class Unbound(m: String) extends Exception {
  val msg = m
}


case class RelabelVar(variable: String) extends Relabeling {
  var sym = Symbol(variable)
  def relabellingString: String = "[" + sym.name + "]"
  def getLabel(s: Symbol) = getMap().get(s)
  
  override
  def hashCode() = sym.hashCode()
  
  def getMap() = CCSComponent.environment.getRelabel(sym).getMap
  def getLitRelabel = CCSComponent.environment.getRelabel(sym)
  
  override
  def equals(rhs: Any) = {
    rhs match {
      case rel: RelabelVar => rel.asInstanceOf[RelabelVar].variable == variable
      case _ => false
    }
  }
}


object Relabeling {

  def makeLiteralRelabelling(lst: List[(CCSAction, CCSAction)]): LitRelabel = {
    val labelMap = new HashMap[Symbol, Act]
    for (pair@(to, from) <- lst) {
    // Ignore labelings from a -> a
    if (to != from) {
      pair match {
        case (t: Act, f: Act) => {
          val (key, value) = if (f.asInstanceOf[Act].polarity) {
            (f.asInstanceOf[Act].port, t.asInstanceOf[Act])
          } else {
              (f.asInstanceOf[Act].port, t.asInstanceOf[Act].inverse)
          }
          labelMap.get(key) match {
            case None => labelMap.put(key, value)
            case Some(x) => {
              // Duplicates are just ignored, double entries are alerted.
              if (x != value) throw Error("Error relabelling must be a function!")
            }
          }
        }
        case _ => throw Error("Can't relabel " + from.mkstr)
      }
    }
  }
    new LitRelabel(labelMap)
  }
}